
#ifndef META_INVOKE_HPP
#define META_INVOKE_HPP
#include <type_traits>

#include "meta_alias_argc.hpp"
namespace is_meta_function_trait_detail {
	template<template<class ...> class apply_shape> struct meta_function_template_container {};

	template<class F, class En = void> struct is_meta_function_type : std::false_type {};

	template<class F> struct is_meta_function_type <F, std::void_t<meta_function_template_container<F::template apply>>> : std::true_type {};

}

template<class F> constexpr bool is_meta_function_v = is_meta_function_trait_detail::is_meta_function_type<F>::value;

namespace meta_invoke_detail {
	template<class F, class ...L> 
	struct impl {
		using type = typename F::template apply<L...>;
	};

	template<class F, class TL>
	struct type_list_invoke;

	template<class F, template<class...> class L, class ...Ts>
	struct type_list_invoke<F, L<Ts...>> {
		using type = typename impl<F, Ts...>::type;
	};
}


template<class F, class ...L> requires is_meta_function_v<F>
using meta_invoke = typename meta_invoke_detail::impl<F, L...>::type;


template<class F, class TL>
using meta_list_invoke = typename meta_invoke_detail::type_list_invoke<F, TL>::type;

namespace meta_invoke_if_detail {
	template<bool con, class F, class ...Args>
	struct meta_function_branch
	{
		using type = F;
	};
	template<class F, class ...Args>
	struct meta_function_branch<true, F, Args...>
	{
		using type = meta_invoke<F, Args...>;
	};
} // namespace meta_invoke_if_detail

template<bool con> struct invoke_if
{
	template<class F, class ...Args> using apply = typename meta_invoke_if_detail::meta_function_branch<con, F, Args...>::type;
};


namespace meta_quote
{
  
    template<template<typename> class Template>
    struct unary
    {
        template<typename Arg>
        using apply = Template<Arg>;
    };

 
    template<template<typename, typename> class Template>
    struct binary
    {
        template<typename T, typename U>
        using apply = Template<T, U>;
    };

 
    template<template<typename, typename> class Template, typename Arg>
    struct bind_binary
    {
        template<typename U>
        using apply = Template<Arg, U>;
    };

} // namespace meta_quote

namespace meta_fold_detail {

	template<class T, template<class> class ...Fs>
	struct impl {};

	template<class T, template<class> class F>
	struct impl<T, F> {
		using type = F<T>;
	};

	template<class T, template<class> class First, template<class> class ...Rest>
	struct impl<T, First, Rest...>{
		using type = typename impl<First<T>, Rest...>::type;
	};

	
}

template<class T, template<class>class ...Fs>
using meta_fold = typename meta_fold_detail::impl<T, Fs...>::type;

template<template<class> class ...meta_templates>
struct meta_fold_list {
	template<class T>
	using apply = meta_fold<T, meta_templates...>;
};
#endif // !META_INVOKE_HPP
